using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace Gko.Utils
{
	public class UnmanagedPointer : DisposableBase
	{
		private Action<IntPtr> disposeAction;
		private IntPtr pointer;

		public UnmanagedPointer()
		{
		}

		public UnmanagedPointer(IntPtr value, Action<IntPtr> disposeAction)
		{
			if (value.IsNull()) {
				throw new ArgumentNullException("value");
			}
			if (disposeAction == null) {
				throw new ArgumentNullException("disposeAction");
			}
			this.pointer = value;
			this.disposeAction = disposeAction;
		}

		public bool IsAttached
		{
			get
			{
				return !this.pointer.IsNull();
			}
		}

		protected override void OnDisposeUnmanaged()
		{
			base.OnDisposeUnmanaged();

			if (this.IsAttached) {
				this.disposeAction(this.pointer);
			}
			this.pointer = IntPtr.Zero;
		}

		public virtual IntPtr Detach()
		{
			this.disposeAction = null;
			using (Scope.Create(() => this.pointer = IntPtr.Zero)) {
				return this.pointer;
			}
		}

		/// <exception cref="InvalidOperationException">Throws if have not null pointer (not equals to <see cref="F:System.IntPtr.Zero"/>)</exception>
		/// <exception cref="ArgumentNullException">One of arguments is <c>null</c>.</exception>
		public UnmanagedPointer Attach(IntPtr value, Action<IntPtr> disposer)
		{
			if (this.IsAttached) {
				throw new InvalidOperationException("Can not attach poiner. Already attached. Call Detach() first or create new instance");
			}
			if (value.IsNull()) {
				throw new ArgumentNullException("value");
			}
			if (disposer == null) {
				throw new ArgumentNullException("disposer");
			}
			this.pointer = value;
			this.disposeAction = disposer;
			return this;
		}

		public static implicit operator IntPtr(UnmanagedPointer pointer)
		{
			return pointer.ToIntPtr();
		}

		/// <summary>
		/// Gets the pointer to unmanaged memory.
		/// </summary>
		/// <value>The pointer to unmanaged memory.</value>
		public IntPtr ToIntPtr()
		{
			this.VerifyNotDisposed();
			return this.pointer;
		}
	}
}